package ssau.knyazev.fft.modules.threads;

import java.util.concurrent.CyclicBarrier;

import ssau.knyazev.core.B2Complex;
import ssau.knyazev.core.CComplex;

public class B2ComplexThread implements Runnable{
	protected int idx;
	protected int idy;
	protected int uid;
	protected int blockX;
	protected int blockY;
	private B2Complex[][] data = null;
	private CyclicBarrier barrier = null;
	private B2Complex[] wilist = null;
	private B2Complex[] wjlist = null;
	
	
	public B2ComplexThread(CyclicBarrier barrier, int idx, int idy, int blockX, int blockY, B2Complex[][] data, B2Complex[] wilist, B2Complex[] wjlist){
		this.idx = idx;
		this.idy = idy;
		this.uid = idy*blockX + idx;
		this.data = data;
		this.blockX = blockX;
		this.blockY = blockY;
		this.wilist = wilist;
		this.wjlist = wjlist;
		this.barrier = barrier;
	}
	
	public void run() {
		int N = data.length;
		int size = 0;
		for (int x = data.length; x > 1; x = x >> 1){
			size++;
		}
		int step, step2;
		int borderDown = ((idx+1)*N/blockX);
		int borderRight = ((idy+1)*N/blockY);
		boolean flag;
		for (int q = 0; q < size-1; q++) {
			step = (int)Math.pow(2, q);
			step2 = step*2;
			for (int down = idx*N/blockX; down < borderDown - step; down++) {
				for (int right = idy*N/blockY; right < borderRight - step; right++) {
					flag = (down/step) % 2 + (right/step) % 2 > 0 ? false : true;
					if (flag){
						B2Complex w1 = wilist[(down % step2)*(N/step2)];
						B2Complex w2 = wjlist[(right % step2)*(N/step2)];
						fft2d2x2U0U2(data[down][right], data[down][right + step], data[down + step][right], data[down + step][right + step], w1, w2);
						fft2d2x2U1U3(data[down][right], data[down][right + step], data[down + step][right], data[down + step][right + step], w1, w2);					}
				}
			}
		}
		try {
			barrier.await();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		if (idx == 0 && idy == 0){
			step = (int)Math.pow(2, size-1);
			step2 = step*2;
			for (int down = 0; down < N - step; down++) {
				for (int right = 0; right < N - step; right++) {
					flag = (down/step) % 2 + (right/step) % 2 > 0 ? false : true;
					if (flag){
						B2Complex w1 = wilist[(down % step2)*(N/step2)];
						B2Complex w2 = wjlist[(right % step2)*(N/step2)];
						fft2d2x2U0U2(data[down][right], data[down][right + step], data[down + step][right], data[down + step][right + step], w1, w2);
						fft2d2x2U1U3(data[down][right], data[down][right + step], data[down + step][right], data[down + step][right + step], w1, w2);
					}
				}
			}
		}
		
		
	}

	protected static void fft2d2x2U1U3(B2Complex a00, B2Complex a01, B2Complex a10, B2Complex a11, B2Complex wi, B2Complex wj) {
		B2Complex newa00 = new B2Complex();
		B2Complex newa01 = new B2Complex();
		B2Complex newa10 = new B2Complex();
		B2Complex newa11 = new B2Complex();

		newa00 = B2Complex.sumU1U3(newa00, a00);
		newa10 = B2Complex.sumU1U3(newa10, a00);
		newa01 = B2Complex.sumU1U3(newa01, a00);
		newa11 = B2Complex.sumU1U3(newa11, a00);
		
		B2Complex t = B2Complex.multyU1U3(wi, a10);
		newa00 = B2Complex.sumU1U3(newa00, t);
		newa10 = B2Complex.subU1U3(newa10, t);
		newa01 = B2Complex.sumU1U3(newa01, t);
		newa11 = B2Complex.subU1U3(newa11, t);
		
		t = B2Complex.multyU1U3(wj, a01);
		newa00 = B2Complex.sumU1U3(newa00, t);
		newa10 = B2Complex.sumU1U3(newa10, t);
		newa01 = B2Complex.subU1U3(newa01, t);
		newa11 = B2Complex.subU1U3(newa11, t);
		
		t = B2Complex.multyU1U3(wj, a11);
		t = B2Complex.multyU1U3(wi, t);
		newa00 = B2Complex.sumU1U3(newa00, t);
		newa10 = B2Complex.subU1U3(newa10, t);
		newa01 = B2Complex.subU1U3(newa01, t);
		newa11 = B2Complex.sumU1U3(newa11, t);

		copyValuesU1U3(a00, newa00);
		copyValuesU1U3(a01, newa01);
		copyValuesU1U3(a10, newa10);
		copyValuesU1U3(a11, newa11);
	}
	
	protected static void fft2d2x2U0U2(B2Complex a00, B2Complex a01, B2Complex a10, B2Complex a11, B2Complex wi, B2Complex wj) {
		B2Complex newa00 = new B2Complex();
		B2Complex newa01 = new B2Complex();
		B2Complex newa10 = new B2Complex();
		B2Complex newa11 = new B2Complex();

		newa00 = B2Complex.sumU0U2(newa00, a00);
		newa10 = B2Complex.sumU0U2(newa10, a00);
		newa01 = B2Complex.sumU0U2(newa01, a00);
		newa11 = B2Complex.sumU0U2(newa11, a00);
		
		B2Complex t = B2Complex.multyU0U2(wi, a10);
		newa00 = B2Complex.sumU0U2(newa00, t);
		newa10 = B2Complex.subU0U2(newa10, t);
		newa01 = B2Complex.sumU0U2(newa01, t);
		newa11 = B2Complex.subU0U2(newa11, t);
		
		t = B2Complex.multyU0U2(wj, a01);
		newa00 = B2Complex.sumU0U2(newa00, t);
		newa10 = B2Complex.sumU0U2(newa10, t);
		newa01 = B2Complex.subU0U2(newa01, t);
		newa11 = B2Complex.subU0U2(newa11, t);
		
		t = B2Complex.multyU0U2(wj, a11);
		t = B2Complex.multyU0U2(wi, t);
		newa00 = B2Complex.sumU0U2(newa00, t);
		newa10 = B2Complex.subU0U2(newa10, t);
		newa01 = B2Complex.subU0U2(newa01, t);
		newa11 = B2Complex.sumU0U2(newa11, t);

		copyValuesU0U2(a00, newa00);
		copyValuesU0U2(a01, newa01);
		copyValuesU0U2(a10, newa10);
		copyValuesU0U2(a11, newa11);
	}
	
	protected static void copyValuesU0U2(B2Complex a, B2Complex b) {
		a.setU0(b.getU0());
		a.setU2(b.getU2());
	}
	
	protected static void copyValuesU1U3(B2Complex a, B2Complex b) {
		a.setU1(b.getU1());
		a.setU3(b.getU3());
	}

}